package com.bigknow.appstore.server.db;

import com.mongodb.*;
import com.mongodb.MongoClientOptions.Builder;
import org.springframework.beans.factory.config.AbstractFactoryBean;

import java.util.ArrayList;
import java.util.List;

public class MongoFactoryBean extends AbstractFactoryBean<MongoClient> {

    // 表示服务器列表(主从复制或者分片)的字符串数组
    private String[] serverStrings;
    // mongoDB配置对象
    // 是否主从分离(读取从库)，默认读写都在主库
    private boolean readSecondary = false;

    private String username;
    private String password;
    private boolean auth;
    private int connectionsPerHost = 100;
    private int threadsAllowedToBlockForConnectionMultiplier = 5;
    private int maxWaitTime = 1000 * 60 * 2;
    private int connectTimeout = 1000 * 10;
    private int socketTimeout = 0;
    private boolean socketKeepAlive = false;
    // 设定写策略(出错时是否抛异常)，默认采用SAFE模式(需要抛异常)
    private WriteConcern writeConcern = WriteConcern.SAFE;
    private boolean cursorFinalizerEnabled = true;
    private int heartbeatSocketTimeout = Integer.parseInt(System.getProperty("com.mongodb.updaterSocketTimeoutMS",
            "20000"));

    @Override
    public Class<?> getObjectType() {
        return MongoClient.class;
    }

    @Override
    protected MongoClient createInstance() throws Exception {
        MongoClient mongo = initMongo();

        // 设定主从分离
        if (readSecondary) {
            mongo.setReadPreference(ReadPreference.secondaryPreferred());
        }

        // 设定写策略
        mongo.setWriteConcern(writeConcern);
        return mongo;
    }

    /**
     * 初始化mongo实例
     * 
     * @return
     * @throws Exception
     */
    private MongoClient initMongo() throws Exception {
        // 根据条件创建Mongo实例
        MongoClient mongo = null;
        List<MongoCredential> credentials = null;
        List<ServerAddress> serverList = getServerList();

        Builder builder = MongoClientOptions.builder();
        builder.connectionsPerHost(connectionsPerHost).connectTimeout(connectTimeout)
                .cursorFinalizerEnabled(cursorFinalizerEnabled).maxWaitTime(maxWaitTime)
                .threadsAllowedToBlockForConnectionMultiplier(threadsAllowedToBlockForConnectionMultiplier)
                .socketTimeout(heartbeatSocketTimeout).socketKeepAlive(socketKeepAlive).socketTimeout(socketTimeout)
                .writeConcern(writeConcern);

        if (auth){
            MongoCredential credential = MongoCredential.createScramSha1Credential(username, "admin", password.toCharArray());
            credentials = new ArrayList<>();
            credentials.add(credential);
        }


        if (serverList.size() == 0) {
            mongo = new MongoClient();
        } else if (serverList.size() == 1) {
            if (credentials == null){
                mongo = new MongoClient(serverList.get(0), builder.build());
            }else{
                mongo = new MongoClient(serverList.get(0),credentials, builder.build());
            }
        } else {
            if (credentials == null){
                mongo = new MongoClient(serverList, builder.build());
            }else{
                mongo = new MongoClient(serverList,credentials, builder.build());
            }
        }
        return mongo;
    }

    /**
     * 根据服务器字符串列表，解析出服务器对象列表
     * <p>
     * 
     * @Title: getServerList
     *         </p>
     * 
     * @return
     * @throws Exception
     */
    private List<ServerAddress> getServerList() throws Exception {
        List<ServerAddress> serverList = new ArrayList<ServerAddress>();
        try {
            for (String serverString : serverStrings) {
                String[] temp = serverString.split(":");
                String host = temp[0];
                if (temp.length > 2) {
                    throw new IllegalArgumentException("Invalid server address string: " + serverString);
                }
                if (temp.length == 2) {
                    serverList.add(new ServerAddress(host, Integer.parseInt(temp[1])));
                } else {
                    serverList.add(new ServerAddress(host));
                }
            }
            return serverList;
        } catch (Exception e) {
            throw new Exception("Error while converting serverString to ServerAddressList", e);
        }
    }

    public void setServerStrings(String[] serverStrings) {
        this.serverStrings = serverStrings;
    }

    public void setReadSecondary(boolean readSecondary) {
        this.readSecondary = readSecondary;
    }

    public void setConnectionsPerHost(int connectionsPerHost) {
        this.connectionsPerHost = connectionsPerHost;
    }

    public void setThreadsAllowedToBlockForConnectionMultiplier(int threadsAllowedToBlockForConnectionMultiplier) {
        this.threadsAllowedToBlockForConnectionMultiplier = threadsAllowedToBlockForConnectionMultiplier;
    }

    public void setMaxWaitTime(int maxWaitTime) {
        this.maxWaitTime = maxWaitTime;
    }

    public void setConnectTimeout(int connectTimeout) {
        this.connectTimeout = connectTimeout;
    }

    public void setSocketTimeout(int socketTimeout) {
        this.socketTimeout = socketTimeout;
    }

    public void setSocketKeepAlive(boolean socketKeepAlive) {
        this.socketKeepAlive = socketKeepAlive;
    }

    public void setCursorFinalizerEnabled(boolean cursorFinalizerEnabled) {
        this.cursorFinalizerEnabled = cursorFinalizerEnabled;
    }

    public void setHeartbeatSocketTimeout(int heartbeatSocketTimeout) {
        this.heartbeatSocketTimeout = heartbeatSocketTimeout;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public boolean isAuth() {
        return auth;
    }

    public void setAuth(boolean auth) {
        this.auth = auth;
    }
}